// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/scoped_generic.h"

#include <utility>
#include <vector>

#include "testing/gtest/include/gtest/gtest.h"

namespace base {

namespace {

    struct IntTraits {
        IntTraits(std::vector<int>* freed)
            : freed_ints(freed)
        {
        }

        static int InvalidValue()
        {
            return -1;
        }
        void Free(int value)
        {
            freed_ints->push_back(value);
        }

        std::vector<int>* freed_ints;
    };

    typedef ScopedGeneric<int, IntTraits> ScopedInt;

} // namespace

TEST(ScopedGenericTest, ScopedGeneric)
{
    std::vector<int> values_freed;
    IntTraits traits(&values_freed);

    // Invalid case, delete should not be called.
    {
        ScopedInt a(IntTraits::InvalidValue(), traits);
    }
    EXPECT_TRUE(values_freed.empty());

    // Simple deleting case.
    static const int kFirst = 0;
    {
        ScopedInt a(kFirst, traits);
    }
    ASSERT_EQ(1u, values_freed.size());
    ASSERT_EQ(kFirst, values_freed[0]);
    values_freed.clear();

    // Release should return the right value and leave the object empty.
    {
        ScopedInt a(kFirst, traits);
        EXPECT_EQ(kFirst, a.release());

        ScopedInt b(IntTraits::InvalidValue(), traits);
        EXPECT_EQ(IntTraits::InvalidValue(), b.release());
    }
    ASSERT_TRUE(values_freed.empty());

    // Reset should free the old value, then the new one should go away when
    // it goes out of scope.
    static const int kSecond = 1;
    {
        ScopedInt b(kFirst, traits);
        b.reset(kSecond);
        ASSERT_EQ(1u, values_freed.size());
        ASSERT_EQ(kFirst, values_freed[0]);
    }
    ASSERT_EQ(2u, values_freed.size());
    ASSERT_EQ(kSecond, values_freed[1]);
    values_freed.clear();

    // Swap.
    {
        ScopedInt a(kFirst, traits);
        ScopedInt b(kSecond, traits);
        a.swap(b);
        EXPECT_TRUE(values_freed.empty()); // Nothing should be freed.
        EXPECT_EQ(kSecond, a.get());
        EXPECT_EQ(kFirst, b.get());
    }
    // Values should be deleted in the opposite order.
    ASSERT_EQ(2u, values_freed.size());
    EXPECT_EQ(kFirst, values_freed[0]);
    EXPECT_EQ(kSecond, values_freed[1]);
    values_freed.clear();

    // Move constructor.
    {
        ScopedInt a(kFirst, traits);
        ScopedInt b(std::move(a));
        EXPECT_TRUE(values_freed.empty()); // Nothing should be freed.
        ASSERT_EQ(IntTraits::InvalidValue(), a.get());
        ASSERT_EQ(kFirst, b.get());
    }

    ASSERT_EQ(1u, values_freed.size());
    ASSERT_EQ(kFirst, values_freed[0]);
    values_freed.clear();

    // Move assign.
    {
        ScopedInt a(kFirst, traits);
        ScopedInt b(kSecond, traits);
        b = std::move(a);
        ASSERT_EQ(1u, values_freed.size());
        EXPECT_EQ(kSecond, values_freed[0]);
        ASSERT_EQ(IntTraits::InvalidValue(), a.get());
        ASSERT_EQ(kFirst, b.get());
    }

    ASSERT_EQ(2u, values_freed.size());
    EXPECT_EQ(kFirst, values_freed[1]);
    values_freed.clear();
}

TEST(ScopedGenericTest, Operators)
{
    std::vector<int> values_freed;
    IntTraits traits(&values_freed);

    static const int kFirst = 0;
    static const int kSecond = 1;
    {
        ScopedInt a(kFirst, traits);
        EXPECT_TRUE(a == kFirst);
        EXPECT_FALSE(a != kFirst);
        EXPECT_FALSE(a == kSecond);
        EXPECT_TRUE(a != kSecond);

        EXPECT_TRUE(kFirst == a);
        EXPECT_FALSE(kFirst != a);
        EXPECT_FALSE(kSecond == a);
        EXPECT_TRUE(kSecond != a);
    }

    // is_valid().
    {
        ScopedInt a(kFirst, traits);
        EXPECT_TRUE(a.is_valid());
        a.reset();
        EXPECT_FALSE(a.is_valid());
    }
}

// Cheesy manual "no compile" test for manually validating changes.
#if 0
TEST(ScopedGenericTest, NoCompile) {
  // Assignment shouldn't work.
  /*{
    ScopedInt a(kFirst, traits);
    ScopedInt b(a);
  }*/

  // Comparison shouldn't work.
  /*{
    ScopedInt a(kFirst, traits);
    ScopedInt b(kFirst, traits);
    if (a == b) {
    }
  }*/

  // Implicit conversion to bool shouldn't work.
  /*{
    ScopedInt a(kFirst, traits);
    bool result = a;
  }*/
}
#endif

} // namespace base
